#ifndef __FACE__
#define __FACE__

#include "..\Math\Point2.h"
#include "..\Math\Point3.h"
#include "..\Math\Colorb.h"
#include "..\Math\Matrix4.h"
#include "ITexture.h"

#include <algorithm>
#include <vector>

/// <summary>
/// Defines a 3D plane for rendering.
/// </summary>
struct Face
{
public:
	Point3f		Points[4];
	Point2f		TexCoords[4];
	Colorb		Color;
	ITexture	*Texture;
	Point3i		Block;

	Face() { }

	/// <summary>
	/// Initializes a new instance of the <see cref="Face" /> struct.
	/// </summary>
	/// <param name="point0">The first point of the plane.</param>
	/// <param name="point1">The second point of the plane.</param>
	/// <param name="point2">The third point of the plane.</param>
	/// <param name="point3">The fourth point of the plane.</param>
	/// <param name="color">The color of the plane.</param>
	/// <param name="texture">The texture on the plane.</param>
	/// <param name="coords">The top-left of the texcoords for this plane.</param>
	/// <param name="w">The width of the texture used on this plane.</param>
	/// <param name="h">The height of the texture used on this plane.</param>
	Face (const Point3f &point0, const Point3f &point1, const Point3f &point2, const Point3f &point3, const Colorb &color, ITexture *texture, const Point2i &coords, const float &w, const float &h) :
		Color(color),
		Texture(texture)
	{
		if (texture != nullptr)
		{
			float wx = (coords.X * texture->GetScale()) / (float)texture->GetWidth();
			float wy = (coords.Y * texture->GetScale()) / (float)texture->GetHeight();
			float ww = (w * texture->GetScale()) / (float)texture->GetWidth();
			float wh = (h * texture->GetScale()) / (float)texture->GetHeight();
	
			float onePixel = ((1 * texture->GetScale()) / (float)texture->GetWidth()) / 256;

			TexCoords[0].Set(wx + onePixel, wy + onePixel);
			TexCoords[1].Set(wx + ww - onePixel, wy + onePixel);
			TexCoords[2].Set(wx + ww - onePixel, wy + wh - onePixel);
			TexCoords[3].Set(wx + onePixel, wy + wh - onePixel);
		}
		else
			TexCoords[0] = TexCoords[1] = TexCoords[2] = TexCoords[3] = Point2f(0, 0);
		
		Points[0] = point0;
		Points[1] = point1;
		Points[2] = point2;
		Points[3] = point3;
	}

	/// <summary>
	/// Flip the texture coordinates of this face on the X axis.
	/// </summary>
	/// <returns>The same instance of this face.</returns>
	Face &FlipX()
	{
		float lX = TexCoords[0].X, rX = TexCoords[1].X;
		
		TexCoords[0].X = TexCoords[3].X = rX;
		TexCoords[1].X = TexCoords[2].X = lX;

		return *this;
	}

	Face (const Point3f &point0, const Point3f &point1, const Point3f &point2, const Point3f &point3, const Colorb &color, ITexture *texture, const Point2f &coordTL, const Point2f &coordBR, const Point3i &block) :
		Color(color),
		Texture(texture),
		Block(block)
	{
		if (texture != nullptr)
		{
			TexCoords[0].Set(coordTL.X, coordBR.Y);
			TexCoords[1].Set(coordBR.X, coordBR.Y);
			TexCoords[2].Set(coordBR.X, coordTL.Y);
			TexCoords[3].Set(coordTL.X, coordTL.Y);
		}
		else
			TexCoords[0] = TexCoords[1] = TexCoords[2] = TexCoords[3] = Point2f(0, 0);
		
		Points[0] = point0;
		Points[1] = point1;
		Points[2] = point2;
		Points[3] = point3;
	}

	Face Reverse() const
	{
		return Face(Points[3], Points[2], Points[1], Points[0], Color, Texture, TexCoords[0], TexCoords[2], Block);
	}

	Face &Multiply(const Matrix4f &matrix)
	{
		for (int i = 0; i < 4; ++i)
			Points[i] = matrix.transformVec3(Points[i]);

		return *this;
	}
};

#endif